﻿#include <assert.h>
#include <ft2build.h>
#include <klayout_utils/klayout_utils.h>
#include <qlib/geo/text.h>
#include <qlib/utils/font.h>

#include FT_FREETYPE_H

namespace qlib {
std::vector<Vec2dArray> getText_Klayout(std::string text, Anchor anchor, Vec2d position, double size, double rotation) {
  std::vector<std::vector<KlU::Point>> polys = KlU::text_graph(text, {position.x, position.y}, size, rotation, int(anchor));
  std::vector<Vec2dArray> ouptput;
  ouptput.reserve(polys.size());
  for (auto& poly : polys) {
    Vec2dArray temp;
    temp.reserve(poly.size());
    for (auto p : poly) {
      temp.push_back({p.x, p.y});
    }
    ouptput.push_back(temp);
  }
  return ouptput;
}

std::string Text::fontPath = "C:/Windows/Fonts/Arial.ttf";
float Text::fontSizeBase = 1.0;

Text::Text() {
  anchor = Anchor::O;

  rotation = 0;
  magnification = 0;
  x_reflection = false;

  layer = 0;
  datatype = 0;

  owner = nullptr;
  reference_number = -1;

  box = Vec2dArray(2);
}

Text::Text(std::string _text, Vec2d _origin, double _rotation, double _magnification, bool _x_reflection, uint32_t _layer, uint32_t _datatype) {
  text = _text;
  rotation = _rotation;
  origin = _origin;
  magnification = _magnification;
  x_reflection = _x_reflection;
  layer = _layer;
  datatype = _datatype;

  box = Vec2dArray(2);
}
Text::Text(const Text& other) {
  Text::operator=(other);
}

Text& Text::operator=(const Text& other) {
  fontSize = other.fontSize;
  text = other.text;
  origin = other.origin;
  anchor = other.anchor;
  rotation = other.rotation;
  magnification = other.magnification;
  x_reflection = other.x_reflection;
  layer = other.layer;
  datatype = other.datatype;
  repetition = other.repetition;

  properties.resize(other.properties.size());
  for (size_t i = 0; i < properties.size(); i++) {
    properties[i] = Property::create();
    if (other.properties[i]) {
      properties[i] = Property::create();
      *properties[i] = *other.properties[i];
    }
  }

  offset = other.offset;
  textSize = other.textSize;

  box = other.box;
  polys = other.polys;
  reference_number = other.reference_number;

  return *this;
}

void Text::init(std::string _text, double size, Vec2d _origin, double _rotation, uint32_t _layer, uint32_t _datatype) {
  text = _text;
  origin = _origin;
  rotation = _rotation;
  layer = _layer;
  datatype = _datatype;

  box = Vec2dArray(2);
}

Text::s_ptr Text::copy() const {
  auto res = Text::create();
  *res = *this;

  return res;
}

void Text::apply_repetition(TextPtrArray& result) {
  if (repetition.type == RepetitionType::None)
    return;

  Array<Vec2d> offsets = {};
  repetition.get_offsets(offsets);
  repetition.type = RepetitionType::None;

  // Skip first offset (0, 0)
  double* offset_p = (double*)(offsets.data() + 1);
  result.reserve(offsets.size() - 1);

  Text::s_ptr label;
  for (uint64_t offset_count = offsets.size() - 1; offset_count > 0; offset_count--) {
    label = copy();

    label->origin.x += *offset_p++;
    label->origin.y += *offset_p++;

    result.push_back(label);
  }
}

Vec2dArray Text::bounding_box() const {
  Vec2dArray res(2);
  Vec2d& min = res[0];
  Vec2d& max = res[1];

  min.x = min.y = DBL_MAX;
  max.x = max.y = -DBL_MAX;

  auto polygons = to_polygons();
  for (auto& poly : polygons) {
    for (auto& p : poly->points) {
      if (p.x < min.x)
        min.x = p.x;
      if (p.y < min.y)
        min.y = p.y;
      if (p.x > max.x)
        max.x = p.x;
      if (p.y > max.y)
        max.y = p.y;
    }
  }

  return res;
}

void Text::transform(double mag, bool x_refl, double rot, const Vec2d orig) {
  const int r1 = x_refl ? -1 : 1;
  const double crot = cos(rot);
  const double srot = sin(rot);
  const double x = origin.x;
  const double y = origin.y;
  origin.x = orig.x + mag * (x * crot - r1 * y * srot);
  origin.y = orig.y + mag * (x * srot + r1 * y * crot);
  rotation = r1 * rotation + rot;
  magnification *= mag;
  x_reflection ^= x_refl;
}

void Text::translate(double dx, double dy) {
  origin.x += dx;
  origin.y += dy;
}

extern qlib::Vec2d ftVecToVec2d(const FT_Vector& ftVec);
extern void extractOutlineVertices(FT_GlyphSlot slot, Vec2dArray& vertices);

std::vector<Polygon::s_ptr> Text::to_polygons() const {
  Vec2dArray t_box(2);
  Vec2d& min = t_box[0];
  Vec2d& max = t_box[1];

  min.x = min.y = DBL_MAX;
  max.x = max.y = -DBL_MAX;

  // std::vector<Vec2dArray> res = getStrPoints(text, anchor, origin, fontSize, rotation, FontType::Gdstk);
  std::vector<Vec2dArray> texts = getText_Klayout(text, anchor, origin, fontSize, rotation);
  PolygonPtrArray res_polys;

  for (auto& points : texts) {
    auto poly = Polygon::create();
    poly->points = points;
    poly->layer = layer;
    poly->datatype = datatype;
    for (auto& p : poly->points) {
      if (p.x < min.x)
        min.x = p.x;
      if (p.y < min.y)
        min.y = p.y;
      if (p.x > max.x)
        max.x = p.x;
      if (p.y > max.y)
        max.y = p.y;
    }
    res_polys.push_back(poly);
  }

  auto center = box_center(t_box);

  qlib::Vec2d mirro1 = {center.x, center.y + 10000000};
  qlib::Vec2d mirro2 = {center.x, center.y - 10000000};

  for (auto& poly : res_polys) {
    if (x_reflection) {
      poly->mirror(mirro1, mirro2);
    }
  }

  return res_polys;
}

Vec2dArray Text::render_aabb() {
  return box;
}

std::vector<Vec2dArray> Text::render_poly() {
  std::vector<Vec2dArray> res;
  auto polys = to_polygons();
  for (auto p : polys) {
    res.push_back(p->points);
    Vec2dArray box_t = p->bounding_box();
    if (box_t[0].x < box[0].x) {
      box[0].x = box_t[0].x;
    }
    if (box_t[0].y < box[0].y) {
      box[0].y = box_t[0].y;
    }
    if (box_t[1].x > box[1].x) {
      box[1].x = box_t[1].x;
    }
    if (box_t[1].y > box[1].y) {
      box[1].y = box_t[1].y;
    }
  }
  return res;
}

Vec2dArray Text::render_absorp() {
  return Vec2dArray{origin};
}

void Text::rotate(double angle, Vec2d _center) {
  rotation += angle;

  origin -= _center;
  auto t = origin;

  double ca = cos(angle);
  double sa = sin(angle);

  origin.x = t.x * ca - t.y * sa + _center.x;
  origin.y = t.x * sa + t.y * ca + _center.y;
}

void Text::mirror(qlib::Vec2d p0, qlib::Vec2d p1) {
  Vec2d v = p1 - p0;
  double tmp = v.length_sq();
  if (tmp == 0)
    return;

  Vec2d r = v * (2 / tmp);
  Vec2d p2 = p0 * 2;

  Vec2d* p = &origin;
  *p = v * (*p - p0).inner(r) - *p + p2;

  double toler = 0.001;
  if (std::abs(p0.x - p1.x) < toler) {
    x_reflection = !x_reflection;
  } else {
    x_reflection = !x_reflection;
    rotation = 3.14 - rotation;
  }
}

TextParts Text::get_parts() {
  auto res = std::make_tuple(origin);
  return res;
}

Vec2dArray Text::query_parts(GeoPartType type, uint32_t id) {
  auto parts = get_parts();
  if (type == GeoPartType::Center)
    return Vec2dArray{std::get<0>(parts)};
  return Vec2dArray{};
}

void Text::outGdsFile() {
  magnification = fontSize;
}

double Text::getRotation() {
  return rotation;
}

void Text::setRotation(double _rotation) {
  rotation = _rotation;
}

}  // namespace qlib